#ifndef __DECL_H__
#define __DECL_H__

#include "ast.h"
#include "type.h"
#include "symbol.h"

struct specifier_t;
struct ast_decl_t;

/**
	direct-abstract-declarator:
		without identifier
	direct-declarator:
		with identifier		
*/
enum { DEC_ABSTRACT = 0x01, DEC_CONCRETE = 0x02};

/*
 The possble type constructor, used in struct typeDerivList.
 TypeDerivList->ctor:	 POINTER_TO, ARRAY_OF, FUNCTION_RETURN
*/
enum { POINTER_TO, ARRAY_OF, FUNCTION_RETURN };

// struct decl_t: public ast_node_t
// {
//     struct decl_t *dec;  \
//     char *id;                   \
//     type_derive_t* tyDrvList;
// };

/**
	typedef int INT32;
	tdname represent a typedef name.
	id:	name
	level:	the nesting level of the definition point.
		The nesting level of file scope is 0.
		The beginning of s compound statement will increment
		nesting level; the end of a compound statement will 
		decrement nesting level.
	overload:
		Indicate if the typedef name is used as variable instead of 
		a typedef name in nesting scope.
		
		typedef int a;
		int f(int a){
			... 'a' is a parameter instead of typedef name here.
		}
 */
struct tdname_t
{
	char *id = nullptr;
	int level = 0;
	int overload = 0;
	/* see void PostCheckTypedef(void) */
	int overloadLevel = 0;
};

/**
	Type Derivation
		ctor:	type constructor, can be array,function, pointer
		len:		array length
		qual:	pointer qualifier
		sig:		function signature
		next:	pointer to next type derivation list

	int * a[5];
		array of 5 pointer to int

		astPointerDeclarator --> astArrayDeclarator(5) -->  astDeclarator(a)

	The above syntax tree will be produced during syntax parsing.
	The declaration's binding order is similar as operator's priority.
	Identifier's priority is highest, belongs to direct declarator.
	Array and function's priority is same, belongs to array declarator and 
	function declarator respectively.
	Pointer's priority is lowest, belongs to pointer declarator.
		
	
	
	During semantic check ucc will construct the type derivation list 
	from bottom up.
	(pointer to) -->(array of 5) --> NIL

	function  DeriveType(...) will apply the type derivation in the type
	derivation list from left to right for the base type ty, 
	which in essence is in the order of type constructor's priority.
	For int * a[5], the result of type derivation is 
		"array of 5 pointer to int"
	From left to right:
		base type is int
		(1)  use "pointer to", we have "pointer to int"
		(2)  use "array of 5", we have "array of 5" "ponter to int",
			that is , "array of 5 pointer to int"
*/
/*
 Each declarator (Function/Array/Pointer declarator) owns an attribue defined 
 below as struct typeDerivList object.
*/
struct type_derive_t
{
	int ctor = 0;		/* type constructor		pointer/function/array */
	union
	{
		int len;	/* array size */
		int qual;	/* pointer qualifier */
		signature_t* sig = nullptr;	/* function signature */
	};
	struct type_derive_t *next = nullptr;
};

struct dector_t: public ast_node_t
{
    dector_t* dec = nullptr;
    char *id = nullptr;
    type_derive_t* tyDrvList = nullptr;
};

/**
	For example:
		struct st{
			struct{
				int a,b;
			};
			int c;
		} st = {{2},3};
	We use a initData list to represent the above initialization:	
		(offset:0, expr:2)  --> (offset:8,expr:3)
*/
struct init_data_t
{
	int offset = 0;
	struct expr_t* expr = nullptr;
	init_data_t* next = nullptr;
};

/**
	init-declarator:
		declarator
		declarator = initializer
	initializer:
		assignment-expression
		{initializer-list}
		{initializer-list,}
	initializer-list:
		initializer
		initializer-list,initializer

	see	function  ParseInitializer(void)

	{{10,20,30},40,50}
	
	astInitializer		{{10,20,30},40,50}
		lbrace:1
		initials  ---> astInitializer 	{10,20,30}		
						lbrace:1
						initials		---->	astInitializer
												lbrace:0
												expr: 10
												next ----->	astInitializer
																	lbrace:0
																	expr:20
																	next ---->	astInitializer
																					lbrace:1
																					expr:30
																					next:NULL	
						next			----->	  astInitializer
													lbrace:0
													expr:		40
													next	---->	astInitializer
																		lbrace:0
																		expr:	50
																		next:	null
*/
struct ast_init_t: public ast_node_t
{
	/*  left brace  {			1/0		has or not */
	int lbrace = 0;
	union
	{
		ast_node_t* initials = nullptr;		/* when lbrace is 1,	initializer-list */
		struct expr_t* expr;		/* when lbrace is 0, assignment-expression */
	};
	/**
		When we do syntax parsing, we don't know the offset of each initialized
		field, So we use lbrace / initials / expr above.
		After semantics parsing, we know the offset of each initialized field,
		then we record these info in InitData for IR generation.
		only used in semantic check 
	*/
	init_data_t* idata = nullptr;
};

/**
	init-declarator:
			declarator
			declarator = initializer
*/
struct init_dector_t: public ast_node_t
{
	/* declarator */
	dector_t* dec = nullptr;
	/* initializer */
	ast_init_t* init = nullptr;
};

/**
	parameter-declaration:
		declaration-specifiers declarator
		declaration-specifiers abstract-declarator(opt)
*/
struct param_decl_t: public ast_node_t
{
	specifier_t* specs = nullptr;
	dector_t* dec = nullptr;
};

/**
	parameter-type-list:
		parameter-list
		parameter-list, ...
	parameter-list:
		parameter-declaration +	

	something like:
		int , int , int 
		int , int , ...
		in 
		f(int a, int b, int c), g(int a, int b, ...)

		a, b, c  is in Vector 'ids' in struct astFunctionDeclarator 
*/
struct ast_param_type_list_t: public ast_node_t
{
	ast_node_t* param_decls = nullptr;
	int ellipsis = 0;
};

/**
	direct-declarator:
		identifier
		(declarator)
		direct-declarator [constant-expression(opt)]	-----	Array
		direct-declarator (parameter-type-list)	-----	Function
		direct-declarator (identifier-list(opt))	-----	Function
*/
struct fn_dector_t: public dector_t
{
	char** ids = nullptr;
	ast_param_type_list_t* paramTyList = nullptr;
	int partOfDef = 0;
	signature_t* sig = nullptr;
};

struct array_dector_t: public dector_t
{
	struct expr_t* expr = nullptr;
};

/*
 pointer:
 			*	type-qualifier-list(opt) pointer
 type-qualifier-list:
			type-qualifier
			type-qualifier-list	type-qualifier
	type-qualifier:
			const	|	volatile
*/
struct ptr_dector_t: public dector_t
{
	ast_node_t* ty_quals = nullptr;
};

/**
	struct-declarator:
		declarator
		declarator	opt	:	const-expression
	struct Data{
		int 	a;		---->   a
		int	b:3;	---->	b:3
	};
*/
struct struct_dector_t: public dector_t
{
	ast_decl_t* dec = nullptr;
	struct expr_t* expr = nullptr;
};

/**
 struct-declaration:
	 specifier-qualifier-list	 struct-declarator-list
 specifier-qualifier-list:
	 (type-specifier | type-qualifier)+
 struct-declarator-list:
	 struct-declarator, struct-declarator, ....

	 (int | const| ...)+		a,b;

	see function ParseStructDeclaration(void),
		syntactically , we need specifier-qualifier-list here,
		excluding storage class,
		for simplicity, in UCC, we treat specifier-qualifier-list
		as declaration-specifier represented by astSpecifers Object.
 */
struct struct_decl_t: public ast_node_t
{
	specifier_t* specs = nullptr;
	ast_node_t* stDecs = nullptr;
};

/**
	struct-or-union-specifier:
		struct-or-union		identifier(opt)	{struct-declaration-list}
		struct-or-union		identifier
*/
struct struct_specifier: public ast_node_t
{
	char *id = nullptr;
	/*
	 struct-declaration-list,
	 see function ParseStructOrUnionSpecifier()
	*/
	ast_node_t* stDecls = nullptr;
	int hasLbrace = 0;
};

/*
enumerator:		id
id = constant-expression
*/
struct enum_decl_t: public ast_node_t
{
	char *id = nullptr;
	struct expr_t* expr = nullptr;
};

/*
 enum-specifier:		enum	id {enumerator-list}
						enum	id
 enumerator-list:		enumerator +
*/
struct enum_specifier_t: public ast_node_t
{
	char *id = nullptr;
	ast_node_t* enumers = nullptr;
};

struct typedef_t
{
	char *id = nullptr;
	int level = 0;
	int overload = 0;
	int overloadLevel = 0;
};

/*
 even token is treated an abstract tree node ?
 see function ParseDeclarationSpecifiers(),
 storage classes: auto,static, extern,register, ..  are treated as astToken object.
					typedef is a special case, syntactically considered a  storage class. 
*/
struct ast_token_t: public ast_node_t
{
};

/**
	declaration-specifiers:
		1. storage-class-specifier	...		(static auto register...)
		2. type-specifier 		....		(int, void, ...)
		3. type-qualifier		...			(const volatile)
*/
/* (static | int | const| ...) + */
struct specifier_t: public ast_node_t
{
	ast_node_t* storage_class = nullptr;
	ast_node_t* type_qulifier = nullptr;
	ast_node_t* type_specifier = nullptr;
	/* After semantics check ,we know the storage-class */
	int sclass = 0;
	type_t* type = nullptr;
};

/**
	type-name:
		specifier-qualifier-list	abstract-declarator(opt)

			const int (*)() 	-----  is considered as a type name, anonymous
 */
struct ast_type_name_t: public ast_node_t
{
	specifier_t* specs = nullptr;
	dector_t* dec = nullptr;
};
/*
 declaration:	declaration-specifiers 	init-declarator-list
 init-declarator-list:		declarator
							declarator = initializer
 declaration-specifiers:		(static|int|const...)+
 type-qualifier:		const	volatile
 storage-class-specifier:		static | auto | register | extern | typedef
 type-specifier:		int, void , char, struct-or-union-specifier, typedef-name
*/
struct decl_t: public ast_node_t
{
	/* declaration-specifiers:	(staic | int | const | ...) + */
	specifier_t* specs = nullptr;
	/* init-declarator-list:		id, id=300, */
	ast_node_t* initDecs = nullptr;
};

/*
 typedef int INT32	, 	INT32 is a TypedefName,
	@id  	"INT32" 
*/
struct ast_typedef_t: ast_node_t
{
	char *id = nullptr;
	symbol_t* sym = nullptr;
};

/**
	coord:		label's coordinate
	id:			label's name
	In C, the scope of label is whole function.
	The label's reference can appear before label's definition.
	ref:			label's reference count
	defined:	if label is defined
	respBB:		corresponding basic block,
				used by intermediate code generation
	next:		pointer to next label.
*/
struct label_t
{
	location_t loc;
	char *id = nullptr;
	int ref = 0;
	int defined = 0;
	struct basic_block_t* respBB = nullptr;
	struct label *next = nullptr;
};

/**
	function-definition:
		declaration-specifiers(opt) declarator declaration-list(opt) compound-statement

		int	f(a,b,c) int a,b;double c; { ...}
		or
		int	f(int a,int b,double c)  { ...}
		or
	However:
		 function-declaration is considered as a special case of declaration,
		 with function-declarator.

		see function ParseExternalDeclaration();
 */
struct fn_decl_t: public ast_node_t
{
	/* declaration-specifiers(opt) */
	specifier_t* specs = nullptr;
	dector_t* dec = nullptr;
	fn_dector_t* fdec = nullptr;
	/*  declaration-list(opt) */
	ast_node_t* decls = nullptr;
	/* compound-statement */
	ast_node_t* nodes = nullptr;
	function_symbol_t* fsym = nullptr;
	label_t* labels = nullptr;
	void* loops = nullptr;
	void* swtches = nullptr;
	void* breakable = nullptr;
	bool has_return = false;
};

struct stmt_t;

struct unit_t: public ast_node_t
{
    decl_t* decl = nullptr;
	stmt_t* stmt = nullptr;
};

bool is_typedef(const char* id);

decl_t* parse_decl();

ast_node_t* parse_external_decl(void);

void print_decl(ast_node_t* node);

#endif  // __DECL_H__
